home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr49 / 134_01.zip / CPROFILE.C < prev    next >
Text File  |  1993-06-12  |  19KB  |  765 lines

  1. /*    CPROFILE -- BDS `C' Program Profiler    */
  2.  
  3. /*    Copyright (c) 1983 by Kevin B. Kenny
  4.     Released to the BDS `C' Users' Group for non-commercial distribution.
  5.  
  6.     Kevin Kenny
  7.     729-A E. Cochise Dr.
  8.     Phoenix, Arizona   85020
  9. */
  10.  
  11. #define TITLE "CPROFILE version 83-11-14 copyright (c) 1983 by Kevin Kenny\n"
  12.  
  13. #include "bdscio.h"
  14. #include "dio.h"
  15. #include "cmdutil.h"
  16.  
  17. /*    Configuration    */
  18.  
  19. #define TOP 0x7000        /* End of usable memory.  Must be supplied
  20.                    to CLINK or L2 with the '-t' option. */
  21.  
  22. /*    Define some general-purpose pointers    */
  23.  
  24. union ptr {
  25.     unsigned i;        /* Integer representation */
  26.     unsigned * w;        /* Word pointer */
  27.     char * b;        /* Byte pointer */
  28.     };
  29.  
  30. /*    Define a long integer for the LONG.C package.     */
  31.  
  32. struct longint {
  33.     char longcont [4];
  34.     };
  35. #define LONG struct longint    /* Poor man's TYPEDEF */
  36.  
  37. /*    Define the structure of the execution profile table */
  38.  
  39. struct proftab {
  40.     unsigned pfaddr;    /* PC of the RST 6 operation */
  41.     LONG pftimes;        /* Number of times that RST was hit. */
  42.     } * ptent;
  43.  
  44. /*    Define the structure of the beginning of the run-time RST 6
  45.     interceptor/supervisor                        */
  46.  
  47. struct supvsr {
  48.     char supjmp0;        /* A JMP (0xC3) instruction */
  49.     unsigned supbdos;    /* Entry to CP/M BDOS (where the JMP goes) */
  50.     char supname [9];    /* 'cprofile' monogram to detect re-entry */
  51.     char supjmp1;        /* Another JMP (0xC3) instruction */
  52.     unsigned supinit;    /* Initialization entry into supervisor */
  53.     char supjmp2;        /* Another JMP (0xC3) instruction */
  54.     unsigned supboot;    /* Fake warmboot entry into supervisor */
  55.     unsigned suptop;    /* Upper address of the supervisor */
  56.     struct proftab * supent1;    /* Pointer to first entry in profile */
  57.     struct proftab * supnxte;    /* Pointer to next avail entry */
  58.     unsigned supbios;    /* Address to restore to BIOS transfer */
  59.     char supcmd [128];    /* CPROFILE command line saved for reload */
  60.     };
  61. union {
  62.     struct supvsr * sps;    /* Pointer to the supervisor */
  63.     void (* spf) ();
  64.     unsigned spi;        /* Integer value thereof */
  65.     } suprvp;
  66.  
  67. /*    Define the command line options    */
  68.  
  69. struct option optable [5];        /* Place to put the option table */
  70. int tabsize;                /* Size of the profile table needed */
  71. int pagelen;                /* Printer page length */
  72. int nsrcfs;                /* Number of source files supplied */
  73. char * * sfiles;            /* Names of the source files */
  74. char * srcfnptr [2];            /* Default source file pointer */
  75. char srcfnam [20];            /* Default source file */
  76.  
  77. /*    Define the I/O environment    */
  78.  
  79. char * prgnam;                /* Name of the program being analyzed*/
  80. char comfnam [20];            /* Name of the .COM file */
  81. char comfile [134];            /* I/O stream for the .COM file */
  82. char symfnam [20];            /* Name of the .SYM file */
  83. FILE symfile;                /* CHARIO stream for the .SYM file */
  84. FILE srcfile;                /* Input stream for the source file */
  85.  
  86. /*    Working storage        */
  87.  
  88. int hisargc;                /* ARGC of profiled program */
  89. char ** hisargv;            /* ARGV of profiled program */
  90. char pass2;                /* Flag = TRUE iff in analysis pass */
  91. char fnname [9];            /* Name of function being analyzed */
  92. int stmt;                /* Statement number within function */
  93. int line;                /* Line number within function */
  94. char symfnnam [9];            /* Last function read from .SYM */
  95. unsigned symtopad;            /* Upper address of last .SYM funct */
  96. char symend;                /* Flag == TRUE if at end of .SYM */
  97. char sfopen;                /* Flag == TRUE if a source file open*/
  98. char seof;                /* FLAG == TRUE at end of source file*/
  99. int slevel;                /* Depth of {} nesting in source */
  100. char sname [33];            /* Name of a symbol in source */
  101. char infunct;                /* Flag == TRUE if in funct in src */
  102. int sline;                /* Relative line # in source funct. */
  103. char slopen;                /* Flag == TRUE if in source line */
  104. int linage;                /* Number of lines on current page */
  105. int pageno;                /* Current page # */
  106.  
  107. /*    Main program    */
  108.  
  109. main (argc, argv)
  110.     int argc;
  111.     char * * argv;
  112.     {
  113.     union ptr bdosp;
  114.  
  115.     /* Test if CPROFILE is active.  If it is, this must be the second pass.
  116.        */
  117.  
  118.     pass2 = FALSE;
  119.     prgnam = argv [1];    /* Rem,ember the program name */
  120.     bdosp.i = 6;        /* Point to the BDOS entry address in lowcore*/
  121.     suprvp.spi = *bdosp.w;    /* Get the BDOS entry address (top of TPA) */
  122.     if (!strcmp (suprvp.sps -> supname, "cprofile")) pass2 = TRUE;
  123.  
  124.     /*    Scan the command line, separating CPROFILE's arguments from
  125.         the target program's.    */
  126.  
  127.     hisargv = argv = argv + 1; hisargc = 0;    /* His args start at once */
  128.     --argc;    
  129.     while (argc && strcmp (*argv, ":")) {    /* My args start with a ":" */
  130.         ++argv; --argc; ++hisargc;
  131.         }
  132.     if (pass2) dioinit (&argc, argv);
  133.  
  134.     /* Process command line options    */
  135.  
  136.     getopts (argc, argv);
  137.  
  138.     /* Call appropriate analysis routines */
  139.  
  140.     if (pass2) {
  141.         /* Second pass -- call the analyzer */
  142.  
  143.         analyze (argc, argv);
  144.         }
  145.  
  146.     else {
  147.         /* First pass -- install supervisor and call in program to
  148.             be profiled */
  149.  
  150.         if (!hisargc) {
  151.             fprintf (STD_ERR, "Command must be supplied.\n");
  152.             goto synerr;
  153.             }
  154.         fprintf (STD_ERR, TITLE);
  155.         install ();
  156.         fprintf (STD_ERR, "<<< Loading target program. >>>\n");
  157.         chain (hisargc, hisargv, &(suprvp.sps -> supjmp1));
  158.         }
  159. synerr:
  160.     dioflush ();
  161.     }
  162.  
  163. /*    Process command line options    */
  164.  
  165. getopts (argc, argv)
  166.     int argc;
  167.     char ** argv;
  168.     {
  169.     char error;            /* Flag = TRUE iff command erroneous */
  170.     union {
  171.         int ii;
  172.         char * si;
  173.         char * * msi;
  174.         } info;
  175.     
  176.     /* Define the available options */
  177.  
  178.     initv (optable, EOF,
  179. #define STRG_OPT -1
  180.         "Linecount", NVAL_KWD,
  181. #define LCNT_OPT 0
  182.         "PageLength", NVAL_KWD,
  183. #define PLEN_OPT 1
  184.         "SourceFiles", MSVL_KWD,
  185. #define SFIL_OPT 2
  186.         EOF);
  187.  
  188.     ++argv; --argc;            /* Discard colon separator */
  189.  
  190.     error = FALSE;
  191.     tabsize = 512 * sizeof ptent;
  192.     pagelen = 58;
  193.     srcfnptr [1] = & srcfnam;
  194.     strcpy (srcfnam, hisargv [0]);
  195.     makeext (srcfnam, "C");
  196.     sfiles = & srcfnptr;
  197.     nsrcfs = 1;
  198.  
  199.     while (argc > 0) {
  200.         switch (procarg (&argc, &argv, optable, &info)) {
  201.             case STRG_OPT:
  202.                 break;        /* Discard redirection */
  203.             case LCNT_OPT:
  204.                 tabsize = info.ii * sizeof (* ptent);
  205.                 break;
  206.             case PLEN_OPT:
  207.                 pagelen = info.ii;
  208.                 break;
  209.             case SFIL_OPT:
  210.                 nsrcfs = * (sfiles = info.msi);
  211.                 break;
  212.             default:
  213.                 error=TRUE;
  214.             }
  215.         }
  216.     
  217.     if (error || hisargc < 1) {
  218.         showsyntax ("cprofile <command and options> : [>filename]",
  219.                 optable);
  220.         quit ();
  221.         }
  222.     }
  223.  
  224.  
  225. /*    Install CPROFILE in the top of the TPA    */
  226.  
  227. install () {
  228.     struct ptr p, q;        /* Working pointer */    
  229.     void superv ();            /* The supervisor's code */
  230.     unsigned * supervrl ();        /* The supervisor's relocation table */
  231.     unsigned suprvs;        /* Size of the supervisor */
  232.     unsigned offset;        /* Relocation factor for supervisor */
  233.     unsigned nrelocs;        /* Number of relocated words in sup */
  234.  
  235.     /* Find the top of the TPA.  Using this, the size of the supervisor,
  236.        and the size of the profile table, find where the supervisor origin
  237.        will be.  */
  238.  
  239.     p.i = 6;            /* Get address of BDOS jump + 1 */
  240.     p.i = *p.w;            /* Get address of TPA top. */
  241.     suprvp.spf = &superv;        /* Point to current supervisor origin*/
  242.     suprvs = suprvp.sps -> suptop - suprvp.spi;    
  243.                     /* Get its length */
  244.     p.i = (p.i - suprvs - tabsize) & 0xFF00;
  245.                     /* Find new supervisor origin */
  246.     if (p.i < TOP) {
  247.         fprintf (STD_ERR, 
  248.             "Profile table overlays CPROFILE; use smaller -L\n");
  249.         quit ();
  250.         }
  251.  
  252.     /* Move the supervisor into place */
  253.  
  254.     offset = p.i - suprvp.spi;
  255.     movmem (suprvp.spi, p.i, suprvs);
  256.     suprvp.spi = p.i;
  257.     
  258.     /* Relocate the supervisor */
  259.  
  260.     p.w = supervrl ();        /* Get relocation table */
  261.  
  262.     nrelocs = *p.w++;
  263.     while (nrelocs--) {
  264.         q.i = suprvp.spi + *p.w++;    /* Get a relocation address */
  265.         *q.w += offset;            /* Relocate it */
  266.         }
  267.     
  268.     /*    Link the BDOS transfer through the supervisor */
  269.  
  270.     p.i = 6;
  271.     suprvp.sps -> supbdos = *p.w;
  272.     *p.w = suprvp.spi;
  273.  
  274.     /*    Link the BIOS warmboot transfer through the supervisor */
  275.  
  276.     p.i = 1;
  277.     p.i = *p.w + 1;
  278.     suprvp.sps -> supbios = *p.w;
  279.     *p.w = &(suprvp.sps -> supjmp2);
  280.  
  281.     /*    Record the CPROFILE command line in the supervisor for reload*/
  282.  
  283.     movmem (0x0080, &(suprvp.sps -> supcmd), 128);
  284.     }
  285.  
  286. /*    Analyze the results of a profiled run    */
  287.  
  288. analyze (argc, argv)
  289.     int argc;
  290.     char ** argv;
  291.     {
  292.     int pfcomp ();
  293.     struct proftab * tent;
  294.  
  295.     /* Restore linkage to real BDOS */
  296.  
  297.     deinstal ();
  298.     fprintf (STD_ERR, "<<< Starting analysis phase. >>>\n");
  299.     
  300.     /* Open needed files for analysis */
  301.  
  302.     openfiles ();
  303.  
  304.     /* Initialize source file reader */
  305.  
  306.     seof = infunct = sfopen = FALSE;
  307.     slevel = sline = 0;
  308.  
  309.     /* Sort the profile data by address */
  310.  
  311.     qsort (suprvp.sps -> supent1, 
  312.            suprvp.sps -> supnxte - suprvp.sps -> supent1,
  313.            sizeof * tent,
  314.                pfcomp);
  315.  
  316.     /* Display the profile */
  317.  
  318.     sopen ();            /* Open up the first source file */
  319.     linage = 0; pageno = 0; begline (); slopen = FALSE;
  320.     for (tent = suprvp.sps -> supent1;
  321.           tent < suprvp.sps -> supnxte;
  322.          ++tent) {
  323.         doprent (tent);
  324.         }
  325.     }
  326.  
  327. /*    De-install the run-time supervisor    */
  328.  
  329. deinstal () {
  330.     union ptr p;            /* Working pointer */
  331.  
  332.     p.i = 6;            /* Set pointer to BDOS entry point */
  333.  
  334.     suprvp.spi = *p.w;        /* Find supervisor entry */
  335.  
  336.     *p.w = suprvp.sps -> supbdos;    /* Relink the BDOS transfer correctly*/
  337.  
  338.     }
  339.  
  340. /*    Open .COM and .SYM files in analysis pass    */
  341.  
  342. openfiles () {
  343.     
  344.     /* Open the .SYM file */
  345.  
  346.     strcpy (symfnam, prgnam);
  347.     makeext (symfnam, "SYM");
  348.     if (fopen (symfnam, symfile) == ERROR) {
  349.         fprintf (STD_ERR, "Can't open %s: %s\n", 
  350.                     symfnam, errmsg (errno ()));
  351.         quit ();
  352.         }
  353.     strcpy (symfnnam, "C.CCC");
  354.     if (frdstr (symfile, symfnnam) == ERROR) {
  355.         fprintf (STD_ERR, "Enpty .SYM file %s.\n", symfnam);
  356.         symend=TRUE;
  357.         symtopad = 0xFFFF;
  358.         }
  359.  
  360.     /* Open the .COM file */
  361.  
  362.     strcpy (comfnam, prgnam);
  363.     makeext (comfnam, "COM");
  364.     if (copen (comfile, comfnam) == ERROR) {
  365.         fprintf (STD_ERR, "Can't open %s: %s\n", 
  366.                     comfnam, errmsg (errno ()));
  367.         quit ();
  368.         }
  369.     }
  370.  
  371. /*    Display one profile entry    */
  372.  
  373. doprent (tent)
  374.     struct proftab * tent;
  375.     {
  376.     char tcount [12];        /* Text for count of times executed */
  377.  
  378.     tcseek (comfile, tent -> pfaddr - 0x0100, 0, comfnam);
  379.                     /* Read the RST 6 parameter */
  380.     tcread (comfile, &line, 2, comfnam);
  381.  
  382.     stmt = line >> 12; line &= 0x0FFF;    /* Get line number */
  383.  
  384.     if (line > 1) slineno (line - 1);
  385.  
  386.     if (line == 0) {
  387.         begfun (tent -> pfaddr);    /* Begin a new function */
  388.         begline ();
  389.         printf ("%-8s", fnname);
  390.         }
  391.     else {
  392.         begline ();
  393.         printf ("%4d", line);
  394.         if (stmt == 0)
  395.             printf ("    "); /* Print line # . stmt  count */
  396.         else
  397.             printf (".%-2d\t", stmt);
  398.         }
  399.     
  400.     ltoa (tcount, tent -> pftimes);
  401.     printf ("%7s\t", tcount);
  402.  
  403.     slineno (line);            /* Print source line */
  404.     }
  405.  
  406. /*    Begin a new function in profile    */
  407.  
  408. begfun (addr) 
  409.     unsigned addr;            /* Starting address of function + 1 */
  410.     {
  411.     while (addr >= symtopad && !symend) {
  412.         nxfsym ();
  413.         }
  414.     strcpy (fnname, symfnnam);
  415.     sfunct (fnname);
  416.     }
  417.  
  418. /*    Compare addresses of two entries in the profile table    */
  419.  
  420. int pfcomp (t1, t2)
  421.     struct proftab *t1, *t2;
  422.     {
  423.     if (t1 -> pfaddr > t2 -> pfaddr) return (1);
  424.     else if (t1 -> pfaddr == t2 -> pfaddr) return (0);
  425.     else return (-1);
  426.     }
  427.  
  428. /*    Read the next function from the .SYM file    */
  429.  
  430. nxfsym () {
  431.     if (symend) return;
  432.     if (frdstr (symfile, symfnnam) == ERROR
  433.      || frdhex (symfile, &symtopad) == ERROR) {
  434.         symend = TRUE;
  435.         symtopad = 0xFFFF;
  436.         fclose (symfile);
  437.         }
  438.     }
  439.  
  440. /*    Locate a function on the source file.  Function is presumed to
  441.     start when its name appears outside curly-braces. */
  442.  
  443. sfunct (s)
  444.     char * s;                /* Function name */
  445.     {
  446.     do {
  447.         if (seof) return;    /* End of file; no hope. */
  448.         sscan();        /* Look for the name. */
  449.         } while (slevel != 0 || strcmp (sname, s));
  450.     infunct = TRUE;
  451.     sline = 1;            /* This is first line of function. */
  452.     while (slevel == 0 && !seof) sscan ();    /* Find the open brace */
  453.     seol ();            /* Advance to a line break. */
  454.     }
  455.  
  456. /*    Advance to the end of a function by balancing curly-braces. */
  457.  
  458. sendfn () {
  459.     infunct = FALSE;
  460.     while (slevel > 0 && !seof) sscan ();    /* Eat tokens 'til balanced. */
  461.     seol ();            /* Advance to a line break. */
  462.     }
  463.  
  464. /*    Locate a line within the current function. */
  465.  
  466. slineno (l)
  467.     int l;                /* Line number to find. */
  468.     {
  469.     while (sline != 0 && sline <= l) {    /* Advance */    
  470.         sscan ();
  471.         seol ();
  472.         }
  473.     }
  474.  
  475. /*    Advance source file to a line break    */
  476.  
  477. seol () {
  478.     while (slopen) sscan ();
  479.     }
  480.  
  481. /*    Scan a token out of the source file    */
  482.  
  483. sscan () {
  484.     int c;            /* Current character from file */
  485.  
  486.     c = sgetc ();    /* Get a character */
  487.     if (isalpha (c) || c == '_') 
  488.         ssymb (c);    /* Scan an identifier if that's what we have */
  489.     else {
  490.         sputc (c);    /* Flush the current character to output */
  491.         switch (c) {
  492. case '/':        scomt ();        /* Process comments */
  493.             break;
  494. case '{':        ++slevel;        /* Balance curly braces */
  495.             break;
  496. case '}':        --slevel; /* NO BREAK HERE */
  497. case ';':        if (!infunct) sline = 0;    /* Reset relative line
  498.                             when leaving function*/
  499.             break;
  500. case '\'':
  501. case '"':        squote (c);        /* Process quoted strings */
  502.             break;
  503. case '\n':        if (sline) ++sline;    /* Maintain relative line # */
  504.             break;
  505. case CPMEOF:
  506. case EOF:        seof = TRUE;        /* Handle EOF */
  507.             slopen = FALSE;
  508.             sline = 0;
  509.             break;
  510.             }
  511.         }
  512.     }
  513.  
  514. /*    Read an identifier from source file     */
  515.  
  516. ssymb (c)
  517.     int c;
  518.     {
  519.     char * symp;            /* Pointer to symbol being scanned */
  520.     
  521.     symp = & sname;            /* Initialize scan pointer */
  522.  
  523.     do {
  524.         if (slevel == 0) *symp++ = toupper (c);    
  525.                         /* Accumulate character */
  526.         sputc (c);
  527.         c = sgetc ();        /* Get next character */
  528.         } while (isalpha(c) || isdigit (c) || c == '_');
  529.     ungetc (c, srcfile);        /* Push back char that stopped scan */
  530.     if (slevel == 0) *symp++ = '\0';    /* End the symbol */
  531.     sname [8] = '\0';        /* Truncate it to 8 bytes */
  532.     }
  533.  
  534. /*    Read a quoted string from source file     */
  535.  
  536. squote (c)
  537.     int c;
  538.     {
  539.     int c2;
  540.     c2 = c;
  541.     do {
  542.         if (c2 == '\\') sputc (sgetc ());    /* Do escapes */
  543.         c2 = sputc (sgetc ());    /* Next char */
  544.         } while (c2 != c && c2 != CPMEOF && c2 != EOF);
  545.     }
  546.  
  547. /*    Read a 'C' comment from source file     */
  548.  
  549. scomt () {
  550.     int c;
  551.     if ((c = sgetc ()) != '*') {    /* Really a comment? */
  552.         ungetc (c, srcfile);        /* No, put it back */
  553.         return;
  554.         }
  555.     sputc (c);
  556.     do {
  557.         while ((c = sputc (sgetc ())) != '*') { /* Find '*' */
  558.             if (c == EOF || c == CPMEOF) return;
  559.             if (c == '/') scomt ();    /* Handle nested comments */
  560.             }
  561.         if ((c = sgetc ()) == '*')     /* Handle ... ***/
  562.             ungetc (c, srcfile);
  563.         else sputc (c);
  564.         } while (c != '/' && c != EOF && c != CPMEOF);
  565.     }
  566.  
  567. /*    Read a character from the stack of source files    */
  568.  
  569. int sgetc () {
  570.     int c;
  571.  
  572.     /* Open a file if necessary */
  573.  
  574.     if (!sfopen) {
  575.         if (sopen () == EOF) return (EOF);
  576.         return (sgetc ());
  577.         }
  578.     c = getc (srcfile);
  579.     if (c == EOF || c == CPMEOF) {
  580.         fclose (srcfile);
  581.         sfopen = FALSE;
  582.         linage = 0;
  583.         return (sgetc ());
  584.         }
  585.     else return (c);
  586.     }
  587.  
  588. /*    Open a source file    */
  589.  
  590. int sopen () {
  591.     seof = TRUE;
  592.     if (nsrcfs-- <= 0) return (EOF);
  593.     if (fopen (*++sfiles, srcfile) == ERROR) {
  594.         fprintf (STD_ERR, "Can't open source file %s:%s\n",
  595.                 *sfiles, errmsg (errno ()));
  596.         return (sopen ());
  597.         }
  598.     seof = FALSE;
  599.     sfopen = TRUE;
  600.     return (OK);
  601.     }
  602.  
  603. /*    Copy a source character to the output file    */
  604.  
  605. int sputc (c)
  606.     int c;
  607.     {
  608.     if (!slopen) {            /* Begin a new comment line */
  609.         begline ();
  610.         printf ("\t\t");
  611.         }
  612.     if (c != EOF && c != CPMEOF && c != '\n' && c != '\r')
  613.         printf ("%c", c);    /* Put the character */
  614.     if (c == '\n') slopen = FALSE;    /* Close source line */
  615.     return (c);
  616.     }
  617.     
  618. /*    Read a whitespace-terminated string from a file    */
  619.  
  620. int frdstr (file, string)
  621.     char * file;
  622.     char * string;
  623.     {
  624.     int c;
  625.     if (skipwh (file) == ERROR) return (ERROR);
  626.     while ((c = kgetc (file)) != EOF && c != CPMEOF && !isspace (c))
  627.         *string++ = c;
  628.     *string++ = '\0';
  629.     return (OK);
  630.     }
  631.  
  632. /*    Read a hex number from a file    */
  633.  
  634. int frdhex (file, number)
  635.     char * file;
  636.     int * number;
  637.     {
  638.     int c;
  639.     int d;
  640.     if (skipwh (file) == ERROR) return (ERROR);
  641.     *number = 0;
  642.     while ((d = any ((c = toupper (kgetc (file))), "0123456789ABCDEF"))
  643.                                 != -1) {
  644.         *number = (*number << 4) + d;
  645.         }
  646.     return (OK);
  647.     }
  648.  
  649. /*    Skip whitespace on a file    */
  650.  
  651. skipwh (file)
  652.     char * file;
  653.     {
  654.     int c;
  655.     while (isspace (c = kgetc (file)));
  656.     if (c == EOF || c == CPMEOF) return (ERROR);
  657.     ungetc (c, file);
  658.     return (OK);
  659.     }
  660.  
  661. /*    Kludged 'getc' to make CR-LF pairs into '\n's    */
  662.  
  663. int kgetc (file)
  664.     char * file;
  665.     {
  666.     int c;
  667.     if ((c = getc (file)) != '\r') return (c);
  668.     if ((c = getc (file)) == '\n') return (c);
  669.     ungetc (c, file);
  670.     return ('\r');
  671.     }
  672.  
  673. /*    Begin a line on the listing    */
  674.  
  675. begline () {
  676.     int ac;
  677.     char ** av;
  678.  
  679.     printf ("\n");
  680.  
  681.     /*    Break page if necessary        */
  682.  
  683.     if (linage == 0 || linage >= pagelen) {
  684.         printf ("\fLine\t   Times\tBDS `C' Execution Profile\t");
  685.         if (sfopen) printf ("%s", *sfiles);
  686.         printf ("\tPage %d\n", ++pageno);
  687.         printf ("----\t   -----  < ");
  688.         for (ac=hisargc, av=hisargv;  ac;  --ac, ++av) {
  689.             printf ("%s ", *av);
  690.             }
  691.         printf (">\n\n");
  692.         linage = 3;
  693.         }
  694.     slopen = TRUE;
  695.     ++linage;
  696.     }
  697.  
  698. /*    Locate a character in a string    */
  699.  
  700. int any (c, string)
  701.     char c;
  702.     char * string;
  703.     {
  704.     int i;
  705.     i = 0;
  706.     while (*string) if (c == *string++) return (i); else ++i;
  707.     return (-1);
  708.     }
  709.  
  710. /*    Put an extension on a file name    */
  711.  
  712. makeext (name, ext)
  713.     char * name;        /* Name of the file */
  714.     char * ext;            /* Desired extension */
  715.     {
  716.     do {            /* Find the separatrix */
  717.         if (!*name) {    /* No extension originally */
  718.             *name++ = '.';
  719.             break;
  720.             }
  721.         } while (*name++ != '.');
  722.  
  723.     strcpy (name, ext);    /* Add the extension */
  724.     return (name);
  725.     }
  726.  
  727. /*    Seek on a CHARIO file with error checking    */
  728.  
  729. tcseek (file, addr, flag, name)
  730.     char * file;
  731.     unsigned addr;
  732.     int flag;
  733.     char * name;
  734.     {
  735.     if (cseek (file, addr, flag) == ERROR) {
  736.         fprintf (STD_ERR, "Seek error on %s: %s.\n", 
  737.                         name, errmsg (errno ()));
  738.         quit ();
  739.         }
  740.     }
  741.  
  742. /*    Read a CHARIO file with error checking    */
  743.  
  744. tcread (file, buf, size, name)
  745.     char * file;
  746.     char * buf;
  747.     unsigned size;
  748.     char * name;
  749.     {
  750.     if (cread (file, buf, size) < size) {
  751.         fprintf (STD_ERR, "Read error on %s: %s.\n", 
  752.                         name, errmsg (errno ()));
  753.         quit ();
  754.         }
  755.     }
  756.  
  757. /*    Abort the program    */
  758.  
  759. quit () {
  760.     if (pass2) dioflush ();
  761.     exit ();
  762.     }
  763. keext (name, ext)
  764.     char * name;        /* Name of the file */
  765.     char *